"""
   The class CDALindexer is to declare all the attributes and functions we need to build indices based on any of the concepts
   which appeared on the result text for each patient. The index table will consist of concepts ID and request ID. The request ID
   is the primary key which can be used to find a specific result text.
"""


import MySQLdb
import sys
from time import strftime
import operator
from DatabaseConnector import connection

class CDALIndexer:
    def __new__(cls, *args, **kargs):
        if cls.instance is None:
            cls.instance = object.__new__(cls, *args, **kargs)
        return cls.instance
    
    def __init__(self, start, end):
# sql_element = [{concept list]:{[request id head]:request id tails}}, {concept list: first seen}, {concept list: last seen}, {concept list: frequency}]
# encode a requestid into codehead*1000+codetail, for example, request id 390300 will be stored as 390:300
        self.info_matrix = [{},{},{},{}]
        self.vs = self.getLatestVersion()
        self.seg_start = start
        self.seg_end = end
        self.current_time = self.getCurrentTime()
        self.multiplier = 1000
        self.interval = 10000
        self.seg_size = 1000
        self.num_round = 1
        
    def getLatestVersion(self):
        cursor = connection.cursor()
        cursor.execute("select max(version_id) from snomed.cdal_version")
        tuple = cursor.fetchone()
#        sys.stderr.write( tuple[0], tuple[1]
        return tuple[0]
    
    def getCurrentTime(self):
        return strftime("%Y-%m-%d %H:%M:%S")
           
    def WriteToIndex(self, reqcode, concept_list, datetime):
        for concept in concept_list.keys():
            if self.info_matrix[0].has_key(concept) == False:
                self.info_matrix[0][concept] = {reqcode[0]:reqcode[1]}
                if datetime == "None":
                    self.info_matrix[1][concept] = "9999-09-09 09:09:09"
                    self.info_matrix[2][concept] = "0000-00-00 00:00:00"
                else:
                    self.info_matrix[1][concept] = datetime
                    self.info_matrix[2][concept] = datetime
                self.info_matrix[3][concept] = 1
            else:
                self.InsertReqSeg(reqcode, self.info_matrix[0][concept])
                if datetime <> "None":
                    if self.info_matrix[1][concept] > datetime:
                        self.info_matrix[1][concept] = datetime
                    if self.info_matrix[2][concept] < datetime:
                        self.info_matrix[2][concept] = datetime
                self.info_matrix[3][concept] = self.info_matrix[3][concept] + 1
#            sys.stderr.write( self.info_matrix[0] 
        return
    
    def BatchSQL(self, req_start, req_end):
        try:
            sys.stderr.write( "Entered SQL batch mode..." + "\n")
            cursor = connection.cursor()
            for concept in self.info_matrix[0].keys():
#                sys.stderr.write( concept
#                cursor.execute("select request_id_list, first_seen, last_seen, frequency from snomed.index_concept where concept_id = '" + concept + "'")
##                sys.stderr.write( "select request_id_list, first_seen, last_seen, frequency from snomed.index_concept where concept_id = '" + concept + "'"     
#                if cursor.rowcount <> 0:
##                    sys.stderr.write( concept
#                    tuple = cursor.fetchone()
#                    request_string = tuple[0]
##                    sys.stderr.write( request_string
#                    req_list = self.DecomposeRequest(request_string)
##                    sys.stderr.write( req_list
#                    req_list = self.InsertReq(req_list, concept)
##                    sys.stderr.write( req_list
#                    request_result = self.ComposeRequest(req_list)
##                    sys.stderr.write( request_result
#                    if self.info_matrix[1][concept] > str(tuple[1]):
#                        self.info_matrix[1][concept] = str(tuple[1])
#                    if self.info_matrix[2][concept] < str(tuple[2]):
#                        self.info_matrix[2][concept] = str(tuple[2])
#                    self.info_matrix[3][concept] = self.info_matrix[3][concept] + tuple[3]
#                    sql = "update snomed.index_concept set request_id_list = '" + request_result + "', first_seen = '" + self.info_matrix[1][concept] + "', last_seen ='" + self.info_matrix[2][concept] + "', frequency =" + str(self.info_matrix[3][concept]) + ", created_date_time ='" + self.current_time + "' where concept_id ='" + concept + "'" 
#                    cursor.execute(sql)
##                    sys.stderr.write( sql
#                else:
                request_result = self.ComposeRequest(self.info_matrix[0][concept])
                sql = "insert into snomed.index_concept values('" + concept + "', '" + request_result + "', " + str(self.info_matrix[3][concept]) + ",'" + self.vs + "','" + self.current_time + "', '" + self.info_matrix[1][concept] + "', '" + self.info_matrix[2][concept] + "')"
                cursor.execute(sql)
#                    sys.stderr.write( sql
            sys.stderr.write( "Concept table has been updated" + "\n")
                    
            for i in range(req_start, req_end+1):
                sql = "update hosrep.resulttext_table set flag = 1, version_id = '" + self.vs + "', processed_date_time = '" + self.current_time +"' where RequestID = " + str(i)
                cursor.execute(sql)
#                sys.stderr.write( sql
            sys.stderr.write( "Result table updated..." + "\n")
            
            sql = "insert into snomed.log values('" + self.vs+ "'," + str(self.seg_start) + "," + str(self.seg_end) + "," + str(req_start) + "," + str(req_end) + ",'" + self.current_time +"')"
            cursor.execute(sql)
            sys.stderr.write( sql + "\n")
            sys.stderr.write( "Actions have been recorded in the log..." + "\n")
            connection.commit()
            sys.stderr.write( "Database updated" + "\n")
        except MySQLdb.OperationalError, message:
                    errorMessage = "Error %d:\n%s" % (message[ 0 ], message[ 1 ] )
                    sys.stderr.write( errorMessage + "\n")         
        return
    
    def InsertReqSeg(self, reqcode, segment):
        if segment.has_key(reqcode[0]):
            segment[reqcode[0]] = segment[reqcode[0]] + "," + reqcode[1] 
        else:
            segment[reqcode[0]] = reqcode[1]
#        sys.stderr.write( segment
        return
    
    def DecomposeRequest(self, req_string):
        req_seg_ls = {}
        req_seg = req_string.split(";")
        for req_seg_ in req_seg:
            req_seg_ls[req_seg_.split(":")[0]] = req_seg_.split(":")[1]
        return req_seg_ls
    
    def ComposeRequest(self, req_ls):
        request_id_list = ""
        for req_seg in req_ls.keys():
#            sys.stderr.write( req_seg
#            sys.stderr.write( req_ls[req_seg]
            request_id_list = request_id_list + str(req_seg) + ":" + req_ls[req_seg] + ";"
        length = len(request_id_list)
#        sys.stderr.write( "before slicing:" + request_id_list
        request_id_list = operator.getslice(request_id_list, 0, length-1)
        return request_id_list
    
    def InsertReq(self, req_ls, concept):
        list = req_ls
#        sys.stderr.write( self.info_matrix[0]
        for req_seg in self.info_matrix[0][concept].keys():
            if list.has_key(req_seg):
                temp = list[req_seg].split(",")
                if temp.count(self.info_matrix[0][concept][req_seg]) == 0:
                    list[req_seg] = list[req_seg] + "," + self.info_matrix[0][concept][req_seg]
            else:
                list[req_seg] = self.info_matrix[0][concept][req_seg]
        return list               
    
    def EncodeRequestID(self, reqid):
        codehead = reqid/self.multiplier
        codetail = reqid%self.multiplier
        return (str(codehead), str(codetail))
    
    def DecodeRequestID(self, reqcode):
        return int(reqcode[0])*self.multiplier + int(reqcode[1])
    
    def CleanUp(self):
        self.info_matrix = [{},{},{},{}]
        self.current_time = self.getCurrentTime()
        return
       

if __name__ == "__main__":
    if len(sys.argv) == 1:
        sys.exit("Arguments missed")
    from CDALVersion import CDALVersion
    import Translator
    import TokenMatcher
    conc_list = {}
    cursor = connection.cursor()
    cursor.execute("select count(`RequestID`), max(`RequestID`), min(`RequestID`) FROM hosrep.resulttext_table where `RequestID` >= " + sys.argv[1] + " and `RequestID` <= " + sys.argv[2] + " and `Flag` = 0")
    tuple = cursor.fetchone()
    total_number_text = tuple[0]
    max_number = tuple[1]
    min_number = tuple[2]
    idx = CDALIndexer(int(sys.argv[1]),int(sys.argv[2]))
    ts = Translator.Translator()
#    if total_number_text < idx.num_round:
#        interval = total_number_text
#    else:
#        if total_number_text % idx.num_round == 0:
#            interval = total_number_text/idx.num_round
#        else:
#            interval = total_number_text/idx.num_round + 1
#    interval = 10000
    sys.stderr.write( "Total number: " + str(total_number_text) + "\n")
    sys.stderr.write( "Number of rounds: " + str(idx.num_round) + "\n")
    sys.stderr.write( "Interval: " + str(idx.interval) + "\n")
    for round in range(0, idx.num_round):
        start = round * idx.interval + min_number
        end = (round + 1) * idx.interval + min_number
        if end > max_number:
            end = max_number + 1
        sys.stderr.write( "Round start&end:"+ "\n")
        sys.stderr.write( str(start) + "\t" + str(end) + "\n")
        cursor.execute("select r1.`RequestID`, r1.`ResultText`, r2.`DateTime_Request` FROM hosrep.resulttext_table r1, hosrep.request_table r2 where r1.requestid = r2.requestid and r1.`RequestID` >= " + str(start) + " and r1.`RequestID` < " + str(end) + " and r1.`Flag` = 0")
        req_start = 0
        req_end = 0
        try:
            for i, tuple in enumerate(cursor):
                if i % idx.seg_size ==0:
                    sys.stderr.write( "Round no.:" + str(i/idx.seg_size+1) + "\n")
                    req_start = tuple[0]
                reqcode = idx.EncodeRequestID(tuple[0])
                reqid = tuple[0]
                text = tuple[1]
                datetime = str(tuple[2])
                
                for ((s, e), ids) in TokenMatcher.match_sentence(text.split()):
                    conc_list = ts.translator(ids, conc_list)
                idx.WriteToIndex(reqcode, conc_list, datetime)
                conc_list = {}
                if (i+1)% idx.seg_size == 0 or max_number == i + start -1 or end - start < idx.seg_size or i + start == max_number:
                    req_end = reqid
                    sys.stderr.write( "Req start&end" + "\n")
                    sys.stderr.write( str(req_start) + "\t" + str(req_end) + "\n")
                    idx.BatchSQL(req_start, req_end)
                    idx.CleanUp()
                
        except MySQLdb.OperationalError, message:
                        errorMessage = "Error %d:\n%s" % (message[ 0 ], message[ 1 ] )
                        sys.stderr.write( errorMessage + "\n")
    cursor.close()
    sys.stderr.write( "Program terminated..." + "\n")




